package com.bocloud.cmp.server.utils;

import java.io.IOException;
import java.net.UnknownHostException;
import java.util.Date;
import java.util.Vector;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.snmp4j.CommandResponder;
import org.snmp4j.CommandResponderEvent;
import org.snmp4j.MessageDispatcherImpl;
import org.snmp4j.Snmp;
import org.snmp4j.TransportMapping;
import org.snmp4j.mp.MPv1;
import org.snmp4j.mp.MPv2c;
import org.snmp4j.mp.MPv3;
import org.snmp4j.security.SecurityModels;
import org.snmp4j.security.SecurityProtocols;
import org.snmp4j.security.USM;
import org.snmp4j.smi.Address;
import org.snmp4j.smi.GenericAddress;
import org.snmp4j.smi.OctetString;
import org.snmp4j.smi.TcpAddress;
import org.snmp4j.smi.UdpAddress;
import org.snmp4j.smi.VariableBinding;
import org.snmp4j.transport.DefaultTcpTransportMapping;
import org.snmp4j.transport.DefaultUdpTransportMapping;
import org.snmp4j.util.MultiThreadedMessageDispatcher;
import org.snmp4j.util.ThreadPool;
import org.springframework.beans.factory.annotation.Value;

import com.bocloud.cmp.dao.AlarmDao;
import com.bocloud.cmp.entity.resource.Alarm;
import com.bocloud.cmp.model.ZXAlarmModel;
import com.bocloud.common.utils.DateTools;

/**
 * 
 * @author oywl
 *
 */
public class SnmpTrapReceiver implements CommandResponder {
    private static Logger logger = LoggerFactory.getLogger(SnmpTrapReceiver.class);

    private final String charset = "GBK";

    private MultiThreadedMessageDispatcher dispatcher;
    private Snmp snmp = null;
    private Address listenAddress;
    private ThreadPool threadPool;

    @Value("${snmp.listen.url}")
    private String snmplistenAddress;
    private AlarmDao alarmDao;

    /**
     * 构造函数
     */
    public SnmpTrapReceiver(String snmplistenAddress) {
        this.snmplistenAddress = snmplistenAddress;
    }

    public SnmpTrapReceiver(String snmplistenAddress, AlarmDao alarmDao) {
        this.snmplistenAddress = snmplistenAddress;
        this.alarmDao = alarmDao;
    }

    /**
     * 
     * @throws UnknownHostException
     * @throws IOException
     */
    private void init() throws UnknownHostException, IOException {

        threadPool = ThreadPool.create("TrapPool", 2);
        dispatcher = new MultiThreadedMessageDispatcher(threadPool, new MessageDispatcherImpl());
        listenAddress = GenericAddress.parse(this.snmplistenAddress);
        TransportMapping<?> transport = null;
        if (listenAddress instanceof UdpAddress) {
            transport = new DefaultUdpTransportMapping((UdpAddress) listenAddress);
        } else {
            transport = new DefaultTcpTransportMapping((TcpAddress) listenAddress);
        }
        snmp = new Snmp(dispatcher, transport);
        snmp.getMessageDispatcher().addMessageProcessingModel(new MPv1());
        snmp.getMessageDispatcher().addMessageProcessingModel(new MPv2c());
        snmp.getMessageDispatcher().addMessageProcessingModel(new MPv3());
        USM usm = new USM(SecurityProtocols.getInstance(), new OctetString(MPv3.createLocalEngineID()), 0);
        SecurityModels.getInstance().addSecurityModel(usm);
        snmp.listen();
    }

    public void run() {
        logger.debug("Trap Receiver run !!!");
        try {
            init();
            snmp.addCommandResponder(this);
            logger.debug("Start Listen Trap Message!!!");
        } catch (Exception ex) {
            logger.error("SNMP Linsen Error {}", ex);
        }
    }

    @Override
    public void processPdu(CommandResponderEvent event) {
        if (event == null || event.getPDU() == null) {
            logger.debug("[Warn] ResponderEvent or PDU is null");
            return;
        }
        Vector<? extends VariableBinding> vbVect = event.getPDU().getVariableBindings();
        ZXAlarmModel zxAlarmModel = new ZXAlarmModel();
        for (VariableBinding vb : vbVect) {
            String oid = vb.getOid().toString();
            // logger.info(vb.getOid() + " ==== " + vb.getVariable() + "\n\n");
            if (ZXSnmpConstant.Timeticks.equalsIgnoreCase(oid)) {
                zxAlarmModel.setTimeticks(vb.getVariable().toString());
            } else if (ZXSnmpConstant.mType.equalsIgnoreCase(oid)) {
                zxAlarmModel.setmType(vb.getVariable().toString());
            } else if (ZXSnmpConstant.alarmEventTime.equalsIgnoreCase(oid.substring(0, oid.lastIndexOf(".")))) {
                zxAlarmModel.setAlarmEventTime(vb.getVariable().toString());
            } else if (ZXSnmpConstant.sendNotificationId.equalsIgnoreCase(oid)) {
                zxAlarmModel.setSendNotificationId(vb.getVariable().toInt());
            } else if (ZXSnmpConstant.lastSendNotifcationId.equalsIgnoreCase(oid)) {
                zxAlarmModel.setLastSendNotifcationId(vb.getVariable().toInt());
            } else if (ZXSnmpConstant.systemDN.equalsIgnoreCase(oid)) {
                zxAlarmModel.setSystemDN(vb.getVariable().toString());
            } else if (ZXSnmpConstant.alarmCode.equalsIgnoreCase(oid.substring(0, oid.lastIndexOf(".")))) {
                zxAlarmModel.setAlarmCode(vb.getVariable().toString());
            } else if (ZXSnmpConstant.alarmManagedObjectInstance
                    .equalsIgnoreCase(oid.substring(0, oid.lastIndexOf(".")))) {
                zxAlarmModel.setAlarmManagedObjectInstance(vb.getVariable().toString());
            } else if (ZXSnmpConstant.alarmEventype.equalsIgnoreCase(oid.substring(0, oid.lastIndexOf(".")))) {
                zxAlarmModel.setAlarmEventype(vb.getVariable().toInt());
            } else if (ZXSnmpConstant.alarmProbableCause.equalsIgnoreCase(oid.substring(0, oid.lastIndexOf(".")))) {
                zxAlarmModel.setAlarmProbableCause(vb.getVariable().toInt());
            } else if (ZXSnmpConstant.alarmPerceivedSeverity.equalsIgnoreCase(oid.substring(0, oid.lastIndexOf(".")))) {
                zxAlarmModel.setAlarmPerceivedSeverity(vb.getVariable().toInt());
            } else if (ZXSnmpConstant.alarmSpecificProblem.equalsIgnoreCase(oid.substring(0, oid.lastIndexOf(".")))) {
                zxAlarmModel.setAlarmSpecificProblem(snmpdecode(vb.getVariable().toString()));
            } else if (ZXSnmpConstant.alarmAdditionalText.equalsIgnoreCase(oid.substring(0, oid.lastIndexOf(".")))) {
                zxAlarmModel.setAlarmAdditionalText(snmpdecode(vb.getVariable().toString()));
            } else if (ZXSnmpConstant.alarmNetype.equalsIgnoreCase(oid.substring(0, oid.lastIndexOf(".")))) {
                zxAlarmModel.setAlarmNetype(vb.getVariable().toString());
            } else if (ZXSnmpConstant.alarmIndex.equalsIgnoreCase(oid.substring(0, oid.lastIndexOf(".")))) {
                zxAlarmModel.setAlarmIndex(vb.getVariable().toInt());
            } else if (ZXSnmpConstant.alarmId.equalsIgnoreCase(oid.substring(0, oid.lastIndexOf(".")))) {
                zxAlarmModel.setAlarmId(vb.getVariable().toString());
            } else if (ZXSnmpConstant.alarmCodeName.equalsIgnoreCase(oid.substring(0, oid.lastIndexOf(".")))) {
                zxAlarmModel.setAlarmCodeName(snmpdecode(vb.getVariable().toString()));
            } else if (ZXSnmpConstant.alarmManagedObjectInstanceName
                    .equalsIgnoreCase(oid.substring(0, oid.lastIndexOf(".")))) {
                zxAlarmModel.setAlarmManagedObjectInstanceName(snmpdecode(vb.getVariable().toString()));
            } else if (ZXSnmpConstant.alarmSystemType.equalsIgnoreCase(oid.substring(0, oid.lastIndexOf(".")))) {
                zxAlarmModel.setAlarmSystemType(vb.getVariable().toString());
            } else if (ZXSnmpConstant.alarmNeIP.equalsIgnoreCase(oid.substring(0, oid.lastIndexOf(".")))) {
                zxAlarmModel.setAlarmNeIP(vb.getVariable().toString());
            } else if (ZXSnmpConstant.alarmAck.equalsIgnoreCase(oid.substring(0, oid.lastIndexOf(".")))) {
                zxAlarmModel.setAlarmAck(vb.getVariable().toInt());
            } else if (ZXSnmpConstant.cleiCode.equalsIgnoreCase(oid.substring(0, oid.lastIndexOf(".")))) {
                zxAlarmModel.setCleiCode(vb.getVariable().toString());
            } else if (ZXSnmpConstant.timeZoneID.equalsIgnoreCase(oid.substring(0, oid.lastIndexOf(".")))) {
                zxAlarmModel.setTimeZoneID(vb.getVariable().toString());
            } else if (ZXSnmpConstant.timeZoneOffset.equalsIgnoreCase(oid.substring(0, oid.lastIndexOf(".")))) {
                zxAlarmModel.setTimeZoneOffset(vb.getVariable().toString());
            } else if (ZXSnmpConstant.dSTSaving.equalsIgnoreCase(oid.substring(0, oid.lastIndexOf(".")))) {
                zxAlarmModel.setdSTSaving(vb.getVariable().toString());
            } else if (ZXSnmpConstant.aid.equalsIgnoreCase(oid.substring(0, oid.lastIndexOf(".")))) {
                zxAlarmModel.setAid(vb.getVariable().toString());
            } else if (ZXSnmpConstant.id.equalsIgnoreCase(oid.substring(0, oid.lastIndexOf(".")))) {
                zxAlarmModel.setId(vb.getVariable().toString());
            } else if (ZXSnmpConstant.alarmMocObjectInstance.equalsIgnoreCase(oid.substring(0, oid.lastIndexOf(".")))) {
                zxAlarmModel.setAlarmMocObjectInstance(snmpdecode(vb.getVariable().toString()));
            } else if (ZXSnmpConstant.alarmMeasure.equalsIgnoreCase(oid.substring(0, oid.lastIndexOf(".")))) {
                zxAlarmModel.setAlarmMeasure(vb.getVariable().toString());
            } else if (ZXSnmpConstant.alarmOtherInfo.equalsIgnoreCase(oid.substring(0, oid.lastIndexOf(".")))) {
                zxAlarmModel.setAlarmOtherInfo(vb.getVariable().toString());
            }

        }
        try {
            Alarm alarm = convertAlarm(zxAlarmModel);
            if (null == alarm) {
                return;
            }
            alarmDao.save(alarm);
        } catch (Exception e) {
            logger.error("Save AlarmDao Error {}", e);
        }
    }

    private Alarm convertAlarm(ZXAlarmModel zxAlarmModel) {
        if (null == zxAlarmModel) {
            return null;
        }
        Alarm alarm = new Alarm();
        if (null == zxAlarmModel.getAlarmCodeName()) {
            return null;
        }
        alarm.setName(zxAlarmModel.getAlarmCodeName());
        alarm.setAlarmVal(zxAlarmModel.getAlarmId());
        if (null != zxAlarmModel.getAlarmPerceivedSeverity()) {
            if (1 == zxAlarmModel.getAlarmPerceivedSeverity() || 2 == zxAlarmModel.getAlarmPerceivedSeverity()) {
                alarm.setLevel("DANGER");
            } else {
                alarm.setLevel("WARN");
            }
        }
//        alarm.setType(zxAlarmModel.getAlarmEventype().toString());
        alarm.setTarget(zxAlarmModel.getAlarmAdditionalText());
        if (null != zxAlarmModel.getAlarmEventTime() && null != zxAlarmModel.getdSTSaving()) {
            alarm.setGmtTrigger(DateTools.stringToDateTime(zxAlarmModel.getAlarmEventTime()));
        } else {
            alarm.setGmtTrigger(new Date());
        }
        if (1 == zxAlarmModel.getAlarmAck()) {
            alarm.setStatus("CONFIRMED");
        } else if (2 == zxAlarmModel.getAlarmAck()) {
            alarm.setStatus("UNCONFIRMED");
        } else {
            alarm.setStatus("SOLVED");
        }
        alarm.setConfirmer(zxAlarmModel.getAlarmAdditionalText());// 附加信息 入
                                                                  // 确认人，暂时没有这个字段
        alarm.setGmtConfirm(new Date());
        alarm.setVendorName("中兴SNMP告警");
        alarm.setCreaterId(199L);// 默认199L
        alarm.setGmtCreate(new Date());
        alarm.setType("OPENSTACK");
        // alarm.set 没有告警vendorId vendorName
        return alarm;
    }

    /**
     * snmp 解码
     * 
     * @param hexString
     * @return
     */
    private String snmpdecode(String octetString) {
        try {
            String[] temps = octetString.split(":");
            byte[] bs = new byte[temps.length];
            for (int i = 0; i < temps.length; i++)
                bs[i] = (byte) Integer.parseInt(temps[i], 16);

            return new String(bs, charset);
        } catch (Exception e) {
            return null;
        }
    }

}
